package xyz.zhzby.cyzx.judge.core;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import xyz.zhzby.cyzx.judge.entity.Language;
import xyz.zhzby.cyzx.judge.entity.Submission;
import xyz.zhzby.cyzx.judge.util.NativeLibraryLoader;

import java.util.HashMap;
import java.util.Map;

/**
 * @author zby
 * @date 2022/02/04
 * @Description 本地程序执行器, 用于执行本地应用程序. 包括编译器(gcc)以及用户提交的代码所编译出的程序.
 */
@Component
@Slf4j
public class Runner {

    @Value("${system.username}")
    private String systemUsername;

    @Value("${system.password}")
    private String systemPassword;

    // 加载本地库
    static {
        try {
            NativeLibraryLoader.loadLibrary("JudgerCore");
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error(ex.getMessage());
        }
    }

    /**
     * @Param [submission, workDirectory, baseFileName, inputFilePath, outputFilePath]
     * @Param [评测记录对象, 编译生成结果的目录以及程序输出的目录, 待执行的应用程序文件名(不包含文件后缀), 输入文件路径, 输出文件路径]
     * @Return java.util.Map<java.lang.String, java.lang.Object> [个包含程序运行结果的Map<String, Object>对象]
     * @Author zby
     * @Date 2022/02/04
     * @Description 获取(用户)程序运行结果.
     */
    public Map<String, Object> getRuntimeResult(Submission submission, String workDirectory, String baseFileName, String inputFilePath, String outputFilePath) {
        String commandLine = getCommandLine(submission, workDirectory, baseFileName);
        int timeLimit = getTimeLimit(submission);
        int memoryLimit = getMemoryLimit(submission);

        Map<String, Object> result = new HashMap<>(4, 1);
        String runtimeResultSlug = "SE";
        int usedTime = 0;
        int usedMemory = 0;

        try {
            log.info(String.format("[Submission #%d] 开始以命令行运行： %s (TimeLimit=%d, MemoryLimit=%s)",
                    submission.getId(), commandLine, timeLimit, memoryLimit));
            System.out.println("=======================111==============================");
            Map<String, Object> runtimeResult = getRuntimeResult(commandLine, systemUsername, systemPassword,
                    inputFilePath, outputFilePath, timeLimit, memoryLimit);


            System.out.println(runtimeResult);
            System.out.println("=========================111============================");


            int exitCode = (Integer) runtimeResult.get("exitCode");
            usedTime = (Integer) runtimeResult.get("usedTime");
            usedMemory = (Integer) runtimeResult.get("usedMemory");
            runtimeResultSlug = getRuntimeResultSlug(exitCode, timeLimit, usedTime, memoryLimit, usedMemory);
        } catch (Exception ex) {
            log.error(ex.getMessage());
        }

        result.put("runtimeResult", runtimeResultSlug);
        result.put("usedTime", usedTime);
        result.put("usedMemory", usedMemory);
        return result;
    }

    /**
     * @Param [submission, workDirectory, baseFileName] [评测记录对象, 编译生成结果的目录以及程序输出的目录, 待执行的应用程序文件名(不包含文件后缀)]
     * @Return java.lang.String [待执行的命令行]
     * @Author zby
     * @Date 2022/02/04
     * @Description 获取待执行的命令行.
     */
    private String getCommandLine(Submission submission, String workDirectory, String baseFileName) {
        Language language = submission.getLanguage();
        String filePathWithoutExtension = String.format("%s/%s", workDirectory, baseFileName);
        StringBuilder runCommand =
                new StringBuilder(language.getRunCommand().replaceAll("\\{filename}", filePathWithoutExtension));

        if ("Java".equalsIgnoreCase(language.getName())) {
            int lastIndexOfSpace = runCommand.lastIndexOf("/");
            runCommand.setCharAt(lastIndexOfSpace, ' ');
        }
        return runCommand.toString();
    }

    /**
     * @Param [submission] [评测记录对象]
     * @Return int [最大时间限制]
     * @Author zby
     * @Date 2022/02/04
     * @Description 根据不同语言获取最大时间限制.
     */
    private int getTimeLimit(Submission submission) {
        Language language = submission.getLanguage();
        int timeLimit = submission.getProblem().getTimeLimit();

        if ("Java".equalsIgnoreCase(language.getName())) {
            timeLimit *= 2;
        }
        return timeLimit;
    }

    /**
     * @Param [submission] [评测记录对象]
     * @Return int [最大空间限制]
     * @Author zby
     * @Date 2022/02/04
     * @Description 根据不同语言获取最大空间限制.
     */
    private int getMemoryLimit(Submission submission) {
        return submission.getProblem().getMemoryLimit();
    }

    /**
     * @Param [exitCode, timeLimit, timeUsed, memoryLimit, memoryUsed]
     * @Param [程序退出状态位, 最大时间限制, 程序运行所用时间, 最大空间限制, 程序运行所用空间(最大值)]
     * @Return java.lang.String [程序运行结果的唯一英文缩写]
     * @Author zby
     * @Date 2022/02/04
     * @Description 根据JNI返回的结果封装评测结果.
     */
    private String getRuntimeResultSlug(int exitCode, int timeLimit, int timeUsed, int memoryLimit, int memoryUsed) {
        if (timeUsed >= timeLimit) {
            return "TLE";
        }
        if (memoryUsed >= memoryLimit) {
            return "MLE";
        }
        if (exitCode == 0) {
            return "AC";
        }
        return "RE";
    }

    /**
     * @Param [commandLine, inputFilePath, outputFilePath, timeLimit, memoryLimit]
     * @Param [待执行程序的命令行, 输入文件路径(可为NULL), 输出文件路径(可为NULL), 时间限制(单位ms, 0表示不限制), 内存限制(单位KB, 0表示不限制)]
     * @Return java.util.Map<java.lang.String, java.lang.Object> [一个包含程序运行结果的Map<String, Object>对象]
     * @Author zby
     * @Date 2022/02/04
     * @Description 获取(编译)程序运行结果.
     */
    public Map<String, Object> getRuntimeResult(String commandLine, String inputFilePath, String outputFilePath, int timeLimit, int memoryLimit) {
        Map<String, Object> result = null;
        try {
            System.out.println("========================2222=============================");
            result = getRuntimeResult(commandLine, systemUsername, systemPassword, inputFilePath, outputFilePath, timeLimit, memoryLimit);


            System.out.println(result);
            System.out.println("========================2222=============================");

        } catch (Exception ex) {
            log.error(ex.getMessage());
        }
        return result;
    }

    /**
     * @Param [commandLine, systemUsername, systemPassword, inputFilePath, outputFilePath, timeLimit, memoryLimit]
     * @Param [待执行程序的命令行, 登录操作系统的用户名, 登录操作系统的密码, 输入文件路径(可为NULL), 输出文件路径(可为NULL), 时间限制(单位ms, 0表示不限制), 内存限制(单位KB, 0表示不限制)]
     * @Return java.util.Map<java.lang.String, java.lang.Object> [一个包含程序运行结果的Map<String, Object>对象]
     * @Author zby
     * @Date 2022/02/04
     * @Description 获取程序运行结果.
     */
    public native Map<String, Object> getRuntimeResult(String commandLine, String systemUsername, String systemPassword,
                                                       String inputFilePath, String outputFilePath, int timeLimit,
                                                       int memoryLimit);

}
